#ifndef BINARY_CHUNK_H
#define BINARY_CHUNK_H

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#define MAX_NAME_LEN 64

/* ------------------------------------------- */

/*
 * forward declaratin
 */

typedef uint8_t byte;
typedef uint32_t word;      /* 4 bytes */
typedef uint64_t dword;     /* 8 bytes */
typedef long long int64;
typedef double float64;
typedef struct Constant {
    byte tag;
    union {
        byte nil_v;             /* nil literal */
        byte bool_v;            /* boolean literal */
        float64 num_v;          /* lua number */
        int64 int_v;            /* lua integer */
        byte *short_s;          /* short string */
        byte *long_s;           /* long string */
    };
} const_t;

typedef struct Upvalue upvalue_t;
typedef struct LocVar locvar_t;
typedef struct Protope proto_t;
typedef struct Header hdr_t;
typedef struct BinaryChunk binchk_t;

/* ------------------------------------------- */

/*
 * ====================
 *    Header Layout 
 * ====================
 *
 * --------------------
 * |    signature     |  x0  - x3
 * --------------------
 * |     version      |  x4
 * --------------------
 * |      format      |  x5
 * --------------------
 * |    luac_data     |  x6  - x11
 * --------------------
 * |    cint size     |  x12
 * --------------------
 * |   size_t size    |  x13
 * --------------------
 * |    instr size    |  x14
 * --------------------
 * | lua_integer size |  x15
 * --------------------
 * | lua_number size  |  x16
 * --------------------
 * |     lua_cint     |  x17 - x24
 * --------------------
 * |     lua_cnum     |  x25 - x32
 * --------------------
 */

/* ------------------------------------------- */

/*
 * constans
 */

#define LUA_SIGNATURE       "\x1bLua"               /* 4 bytes */
#define LUAC_VERSION        0x53                    /* 1 byte  */
#define LUAC_FORMAT         0                       /* 1 byte  */
#define LUAC_DATA           "\x19\x93\r\n\x1a\n"    /* 6 bytes */
#define CINT_SIZE           4                       /* 1 byte  */
#define CSIZET_SIZE         8                       /* 1 byte  */
#define INSTRUCTION_SIZE    4                       /* 1 byte  */
#define LUA_INTEGER_SIZE    8                       /* 1 byte  */
#define LUA_NUMBER_SIZE     8                       /* 1 byte  */
#define LUAC_INT            0x5678                  /* 8 bytes */
#define LUAC_NUM            370.5                   /* 8 bytes */

#define TAG_NIL         0x00
#define TAG_BOOLEAN     0X01
#define TAG_NUMBER      0x03
#define TAG_INTEGER     0x13
#define TAG_SHORT_STR   0x04
#define TAG_LONG_STR    0x14

/* ------------------------------------------- */

/*
 * structure 
 */

struct Protope {
    byte srcname[MAX_NAME_LEN]; /* source file name */
    word line_defined;          /* starting line number (cint 4 bytes) */
    word last_line_defined;     /* ending line number (cint 4 bytes) */
    byte num_params;            /* the number of fixed parameters (1 byte) */
    byte is_vararg;             /* check if the funciton has variable parameters (1 byte) */
    byte max_stack_size;        /* the number of registers called stack in lua */
    word *instr_tbl;            /* instructions table and each instruction is 4 bytes */
    const_t *const_tbl;         /* constans table and each constant consists of a tag and its value */
    upvalue_t *upvalue_tbl;     /* each element is 2 bytes */
    proto_t *proto_tbl;         /* protope table, storing sub functions' protopes */
    word *lineinfo_tbl;         /* store each instruction's line number in source file */
    locvar_t *locvar_tbl;       /* store each local variable's name and index of instruction */
    byte *upvalue_name_tbl;     /* store the name in src file of the element in upvalue table */
};

struct Header {
    byte signature[4];  /* the identifier of lua binary chunk file => 1B 4C 75 61 */
    byte version;       /* the version of lua (version 5.2 => 5x16+2=82=0x52 */
    byte format;        /* the format number of binary chunk file (matched with vm) => 00 */
    byte luac_data[6];  /* check if the file is destroyed => 19 93 0D 0A 1A 0A */
    byte cint_size;     /* the size of c integer (4 bytes) => 04 */
    byte sizet_size;    /* the size of size_t (8 bytes) => 08 */
    byte instr_size;    /* the size of vm instruction (4 bytes) => 04 */
    byte luaint_size;   /* the size of lua integer (8 bytes) => 08 */
    byte luanum_size;   /* the size of lua number (double 8 bytes) => 08 */
    int64 luac_int;     /* a lua number for matching the byte order (luaint is 8 bytes) */ 
    float64 luac_num;   /* a lua float for matching the format of float point (luanum is 8 bytes) */
};

struct BinaryChunk {
    hdr_t header;
    byte upvalues_size;
    proto_t main_func;
};

struct Upvalue {
    byte instack;
    byte idx;
};

struct LocVar {
    char varname[MAX_NAME_LEN];
    word start_pc;
    word end_pc;
};

/* ------------------------------------------- */

/*
 * function declaration
 */

proto_t *undump(byte *data);      /* parse the binary chunk */

/* ------------------------------------------- */

#endif  /* BINARY_CHUNK_H */